package jtech.math;


public strictfp class Vec2f {
	
	public float x;
	public float y;

	// Constructor
	public Vec2f() {

	}

	public Vec2f(float x, float y) {
		this.x = x;
		this.y = y;
	}

	public Vec2f(Vec2f other) {
		this.x = other.x;
		this.y = other.y;
	}

	public Vec2f copy() {
		return new Vec2f(this);
	}

	// Setter
	public Vec2f set(float x, float y) {
		this.x = x;
		this.y = y;
		return this;
	}

	public Vec2f set(Vec2f other) {
		this.x = other.x;
		this.y = other.y;
		return this;
	}

	// Add
	public Vec2f add(float x, float y) {
		return new Vec2f(this.x + x, this.y + y);
	}

	public Vec2f add(Vec2f other) {
		return add(other.x, other.y);
	}

	// Sub
	public Vec2f sub(float x, float y) {
		return new Vec2f(this.x - x, this.y - y);
	}

	public Vec2f sub(Vec2f other) {
		return sub(other.x, other.y);
	}

	// Mult
	public Vec2f mult(float x, float y, float value) {
		return new Vec2f(x * value, y * value);
	}
	
	public Vec2f mult(float value) {
		return mult(x, y, value);
	}

	// Div
	public Vec2f div(float x, float y, float value) {
		return new Vec2f(x / value, y / value);
	}

	public Vec2f div(float value) {
		return div(x, y, value);
	}

	// Negate
	public Vec2f negate() {
		return new Vec2f(-x, -y);
	}

	// Length
	public float lengthSq() {
		return x * x + y * y;
	}

	public float length() {
		return (float) Math.sqrt( lengthSq() );
	}

	public Vec2f normalise() {
		float norm = 1.0f / length();
		return new Vec2f(x * norm, y * norm);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Float.floatToIntBits(x);
		result = prime * result + Float.floatToIntBits(y);
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Vec2f other = (Vec2f) obj;
		if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
			return false;
		if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
			return false;
		return true;
	}

	// Utils
	public float dot(Vec2f other) {
		return this.x * other.x + this.y * other.y;
	}
	
	public float angle(Vec2f other) {
		float dot = this.dot(other) / (this.length() * other.length());
		if (dot < -1.0f) {
			dot = -1.0f;
		}
		if (dot > 1.0f) {
			dot = 1.0f;
		}
		return (float) Math.acos(dot);
	}

	public float angle() {
		return (float) Math.atan2(y, x);
	}

	public float theta() {
		float theta = (float) Math.toDegrees( angle() );
		if ((theta < -360) || (theta > 360)) {
			theta = theta % 360;
		}
		if (theta < 0) {
			theta = 360 + theta;
		}
		return theta;
	}

	public Vec2f rotate(float cos, float sin) {
		float newx = x * cos - y * sin;
		float newy = x * sin + y * cos;
		return new Vec2f(newx, newy);
	}
	
    public int sign(Vec2f v2) {
        return (y * v2.x > x * v2.y) ? -1 : 1;
    }
	
    public boolean isZero() {
        return (x * x + y * y) < Float.MIN_VALUE;
    }

    public float distanceSq(Vec2f other) {
        float dx = other.x - this.x;
        float dy = other.y - this.y;
        return dx * dx + dy * dy;
    }
    
    public float distance(Vec2f other) {
    	return (float) Math.sqrt( distanceSq(other) );
    }
    
	@Override
	public String toString() {
		return getClass().getSimpleName() + " {x=" + x + ", y=" + y + ", len=" + length() + ", theta=" + theta() + "}";
	}

	public static Vec2f perpendicular(Vec2f vec) {
	    return new Vec2f(-vec.y, vec.x);
	}
	
	public static Vec2f truncate(Vec2f vec, float limite) {
		float value = limite / vec.length();
		if (value < 1f) {
			vec.x = vec.x * value;
			vec.y = vec.y * value;
		}
		return vec;
	}
	
	public static Vec2f interpolation(Vec2f v1, Vec2f v2, float blend) {
		float newx = (v1.x + (v2.x - v1.x) * blend);
		float newy = (v1.y + (v2.y - v1.y) * blend);
	    return new Vec2f(newx, newy);
	}

	public static Vec2f multAdd(Vec2f c, Vec2f a, float b) {
		c.x = c.x + (a.x * b);
		c.y = c.y + (a.y * b);
		return c;
	}

	public static Vec2f add(Vec2f c, Vec2f a, Vec2f b) {
		c.x = a.x + b.x;
		c.y = a.y + b.y;
		return a;
	}
	
	public static Vec2f sub(Vec2f c, Vec2f a, Vec2f b) {
		c.x = a.x - b.x;
		c.y = a.y - b.y;
		return c;
	}
	
	public static Vec2f add(Vec2f a, Vec2f b) {
		return a.add(b);
	}

	public static Vec2f sub(Vec2f a, Vec2f b) {
		return a.sub(b);
	}
}
